package org.bcncoding.appengine.droiding.client.widget;

import org.bcncoding.appengine.droiding.client.widget.DynaTableDataProvider.RowDataAcceptor;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.InvocationException;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

public class DynaTableWidget extends Composite {

	private static class ErrorDialog extends DialogBox implements ClickHandler {

		private HTML body = new HTML("");

		public ErrorDialog() {
			setStylePrimaryName("DynaTable-ErrorDialog");
			Button closeButton = new Button("Close", this);
			VerticalPanel panel = new VerticalPanel();
			panel.setSpacing(4);
			panel.add(body);
			panel.add(closeButton);
			panel.setCellHorizontalAlignment(closeButton,
					VerticalPanel.ALIGN_RIGHT);
			setWidget(panel);
		}

		public String getBody() {
			return body.getHTML();
		}

		public void onClick(ClickEvent event) {
			hide();
		}

		public void setBody(String html) {
			body.setHTML(html);
		}
	}

	private class NavBar extends Composite implements ClickHandler {

		public final DockPanel bar = new DockPanel();
		public final Button newEntry = new Button("NEW", this);
		public final Button gotoFirst = new Button("&lt;&lt;", this);
		public final Button gotoNext = new Button("&gt;", this);
		public final Button gotoPrev = new Button("&lt;", this);
		public final Button gotoLast = new Button("&gt;&gt;", this);
		public final HTML status = new HTML();

		public NavBar() {
			initWidget(bar);
			HorizontalPanel buttons = new HorizontalPanel();
			HorizontalPanel actions = new HorizontalPanel();
			actions.add(newEntry);
			buttons.add(gotoFirst);
			buttons.add(gotoPrev);
			buttons.add(status);
			buttons.add(gotoNext);
			buttons.add(gotoLast);
			bar.add(actions, DockPanel.WEST);
			bar.add(buttons, DockPanel.EAST);
			bar.setCellHorizontalAlignment(actions, DockPanel.ALIGN_LEFT);
			bar.setCellHorizontalAlignment(buttons, DockPanel.ALIGN_RIGHT);
			// bar.add(status, DockPanel.CENTER);
			// bar.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
			// bar.setCellHorizontalAlignment(status, HasAlignment.ALIGN_RIGHT);
			// bar.setCellVerticalAlignment(status, HasAlignment.ALIGN_MIDDLE);
			// bar.setCellWidth(status, "100%");
			gotoPrev.setEnabled(false);
			gotoFirst.setEnabled(false);
			// FIXME Pasar el 10 a constante de tamaño de filas!!!
			if (getDataRowCount() < 10) {
				gotoNext.setEnabled(false);
				gotoLast.setEnabled(false);
			}
		}

		public void onClick(ClickEvent event) {
			Object source = event.getSource();
			if (source == gotoFirst) {
				startRow = 0;
				refresh();
			} else if (source == gotoPrev) {
				startRow -= getDataRowCount();
				if (startRow < 0) {
					startRow = 0;
				}
				refresh();
			} else if (source == gotoNext) {
				startRow += getDataRowCount();
				refresh();
			} else if (source == gotoLast) {
				countRowData();
			}
		}
	}

	private class RowDataAcceptorImpl implements RowDataAcceptor {
		
		public void accept(Boolean bool) {
			refresh();
		}
		
		public void accept(Integer count) {
			startRow = count;
			refresh();
		}

		public void accept(int startRow, String[][] data) {
			int destRowCount = getDataRowCount();
			int destColCount = grid.getCellCount(0);
			int srcRowIndex = 0;
			int srcRowCount = data.length;
			int destRowIndex = 1; // skip navbar row
			for (; srcRowIndex < srcRowCount; ++srcRowIndex, ++destRowIndex) {
				String[] srcRowData = data[srcRowIndex];
				for (int srcColIndex = 0; srcColIndex < destColCount; ++srcColIndex) {
					if (srcColIndex < 4) {
						String cellHTML = srcRowData[srcColIndex];
						grid.setText(destRowIndex, srcColIndex, cellHTML);
					} else if (srcColIndex == 4) {
						grid.setWidget(destRowIndex, srcColIndex, new Button("EDIT", new ClickHandler() {
							public void onClick(ClickEvent event) {
								//TODO EDIT the row
								//provider.editRow(acceptor);
							}
						}));
					} else if (srcColIndex == 5) {
						final String currentKey = srcRowData[0];
						grid.setWidget(destRowIndex, srcColIndex, new Button("DELETE", new ClickHandler() {
							public void onClick(ClickEvent event) {
								provider.deleteRowData(currentKey, acceptor);
							}
						}));
					}
				}
			}
			boolean isLastPage = false;
			for (; destRowIndex < destRowCount + 1; ++destRowIndex) {
				isLastPage = true;
				for (int destColIndex = 0; destColIndex < destColCount; ++destColIndex) {
					grid.clearCell(destRowIndex, destColIndex);
				}
			}
			navbar.gotoFirst.setEnabled(startRow > 0);
			navbar.gotoPrev.setEnabled(startRow > 0);
			navbar.gotoNext.setEnabled(!isLastPage);
			navbar.gotoLast.setEnabled(!isLastPage);
			setStatusText((startRow + 1) + " - " + (startRow + srcRowCount));
		}

		public void failed(Throwable caught) {
			setStatusText("Error");
			if (errorDialog == null) {
				errorDialog = new ErrorDialog();
			}
			if (caught instanceof InvocationException) {
				errorDialog.setText("An RPC server could not be reached");
				errorDialog.setBody(NO_CONNECTION_MESSAGE);
			} else {
				errorDialog.setText("Unexcepted Error processing remote call");
				errorDialog.setBody(caught.getMessage());
			}
			errorDialog.center();
		}

	}

	// TODO change for a custom error
	private static final String NO_CONNECTION_MESSAGE = "<p>The DynaTable example uses a <a href=\"http://code.google.com/"
			+ "webtoolkit/documentation/com.google.gwt.doc.DeveloperGuide."
			+ "RemoteProcedureCalls.html\" target=\"_blank\">Remote Procedure Call</a> "
			+ "(RPC) to request data from the server.  In order for the RPC to "
			+ "successfully return data, the server component must be available.</p>"
			+ "<p>If you are running this demo from compiled code, the server "
			+ "component may not be available to respond to the RPC requests from "
			+ "DynaTable.  Try running DynaTable in hosted mode to see the demo "
			+ "in action.</p> "
			+ "<p>Click on the Remote Procedure Call link above for more information "
			+ "on GWT's RPC infrastructure.";

	private final RowDataAcceptor acceptor = new RowDataAcceptorImpl();

	private final Grid grid = new Grid();

	private final NavBar navbar = new NavBar();

	private ErrorDialog errorDialog = null;

	private final DockPanel outer = new DockPanel();

	private final DynaTableDataProvider provider;

	private int startRow = 0;

	public DynaTableWidget(DynaTableDataProvider provider, String[] columns,
			String[] columnStyles, int rowCount) {
		if (columns.length == 0) {
			throw new IllegalArgumentException(
					"expecting a positive number of columns");
		}
		if (columnStyles != null && columns.length != columnStyles.length) {
			throw new IllegalArgumentException(
					"expecting as many styles as columns");
		}
		this.provider = provider;
		initWidget(outer);
		outer.add(navbar, DockPanel.NORTH);
		outer.add(grid, DockPanel.CENTER);
		initTable(columns, columnStyles, rowCount);
	}

	public void clearStatusText() {
		navbar.status.setHTML("&nbsp;");
	}

	public void refresh() {
		navbar.gotoFirst.setEnabled(false);
		navbar.gotoPrev.setEnabled(false);
		navbar.gotoNext.setEnabled(false);
		navbar.gotoLast.setEnabled(false);
		setStatusText("Please wait...");
		provider.updateRowData(startRow, grid.getRowCount() - 1, acceptor);
	}

	public void countRowData() {
		setStatusText("Please wait...");
		provider.countRowData(acceptor);
	}

	public void setRowCount(int rows) {
		grid.resizeRows(rows);
	}

	public void setStatusText(String text) {
		navbar.status.setText(text);
	}

	private int getDataRowCount() {
		return grid.getRowCount() - 1;
	}

	private void initTable(String[] columns, String[] columnStyles, int rowCount) {
		grid.resize(rowCount + 1, columns.length);
		for (int i = 0, n = columns.length; i < n; i++) {
			grid.setText(0, i, columns[i]);
			if (columnStyles != null) {
				grid.getCellFormatter().setStyleName(0, i,
						columnStyles[i] + " header");
			}
		}
	}
}
